package net.crisps.cloud.utils.aop;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import net.crisps.cloud.utils.hander.ExceptionHandler;
import net.crisps.cloud.utils.robot_util.service.default_config.AbstractWechatRobotConfigService;
import net.crisps.cloud.utils.vo.ExceptionLogVo;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import static net.crisps.cloud.utils.common_util.CustomStrUtil.*;

/**
 * @author SilenceShine
 * @github <a href="https://github.com/SilenceShine">SilenceShine</a>
 * @since 1.0
 */
@Slf4j
@Aspect
public class ExceptionAspect {

    private static ExecutorService singleExecutor = Executors.newSingleThreadExecutor();


    @Autowired
    AbstractWechatRobotConfigService abstractWechatRobotConfigService;

    @Autowired
    private List<ExceptionHandler> exceptionHandlerList;

    @AfterThrowing(pointcut = "execution(* *..controller..*Controller.*(..))", throwing = "ex")
    public void handleException(JoinPoint point, Exception ex) {
        Collection<Class<? extends Exception>> classes = abstractWechatRobotConfigService.getIgnoreException();
        if (null == classes) return;
        Optional<Class<? extends Exception>> optional = classes.stream()
                .filter(Objects::nonNull)
                .filter(a -> a.isAssignableFrom(ex.getClass()))
                .findAny();
        if (optional.isPresent()) return;
        ExceptionLogVo logVo = initExceptionLogVo(point, ex);
        log.error("ExceptionAspect.handleException:{}", logVo);
        // 如果有附带处理 那么附带处理
        if (null != exceptionHandlerList) {
            for (ExceptionHandler handler : exceptionHandlerList) {
                singleExecutor.execute(() -> {
                    try {
                        handler.process(logVo);
                    } catch (Exception e) {
                        log.error("=======ExceptionHandler====={},{}", handler, e.getMessage());
                    }
                });
            }
        }
    }

    private ExceptionLogVo initExceptionLogVo(JoinPoint point, Exception ex) {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String sysCode = request.getHeader("sysCode");
            ExceptionLogVo exceptionLogVo = new ExceptionLogVo();
            exceptionLogVo.setIp(getRemoteIpAddress(request));
            exceptionLogVo.setServerPort(String.valueOf(request.getServerPort()));
            exceptionLogVo.setRemotePort(String.valueOf(request.getRemotePort()));
            exceptionLogVo.setMethod(request.getMethod());
            exceptionLogVo.setRequestUri(request.getRequestURI());
            exceptionLogVo.setClassName(point.getTarget().getClass().getSimpleName());
            exceptionLogVo.setParams(getParams(point));
            exceptionLogVo.setExceptionStr(errorInfoToString(ex));
            exceptionLogVo.setSysCode(sysCode);
            return exceptionLogVo;
        } catch (Exception e) {
            log.error("======WeChatRobotAspect=====initRequestInfo==={}", e.getMessage(), e);
        }
        return null;
    }

    public static String getRemoteIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (isNotBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }

        if (isNotBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }

        if (isNotBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }

        if (isNotBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }

        if (isNotBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (isBlank(ip)) {
            return ip;
        } else {
            String[] newIps = ip.split(COMMA);
            return newIps[0];
        }
    }

    private String getParams(JoinPoint point) {
        return JSON.toJSONString(Arrays.stream(point.getArgs())
                .filter(a -> !(a instanceof ServletRequest))
                .filter(a -> !(a instanceof ServletResponse))
                .filter(a -> !(a instanceof MultipartFile))
                .collect(Collectors.toList()));
    }

    public static String errorInfoToString(Throwable e) {
        try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
            e.printStackTrace(pw);
            pw.flush();
            sw.flush();
            return sw.toString().substring(0, 3000);
        } catch (Exception ignored) {
            log.error("======ExceptionAspect,errorInfoToString====转换异常信息失败=======", e);
        }
        return "==转换异常信息失败=" + e.getMessage();
    }


}
